package testing;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.LinkedList;
import java.util.Random;

import com.thesis.algorithm.*;
import com.thesis.utils.BusControl;
import com.thesis.utils.CoordinateConversion;
import com.thesis.utils.CONSTANTS;

import java.util.Hashtable;

public class DataValidator {

	public class LacLatLng {
		int lac;
		double lat;
		double lng;

		public LacLatLng(int lac, double lat, double lng) {
			this.lac = lac;
			this.lat = lat;
			this.lng = lng;
		}
	}

	HMM mHMM;
	Hashtable<Integer, LacLatLng> cellIDwLac = new Hashtable<Integer, LacLatLng>();

	public void checkLacChange(LinkedList<Device> devices) {
		int lac;
		int id;
		double distance;
		CoordinateConversion cc = new CoordinateConversion();
		for (Device d : devices) {
			for (Sample s : d.sampleList) {
				for (int i = 0; i < s.cellList.size() && i < 1; i++) {
					Cell c = s.cellList.get(i);
					lac = Integer.parseInt(c.lac);
					id = Integer.parseInt(c.id);
					if (cellIDwLac.containsKey(id)) {
						if (lac != cellIDwLac.get(id).lac) {
							System.err.println("FILE: "
									+ d.filename
									+ " cellID: "
									+ id
									+ " lac1: "
									+ lac
									+ " lac2: "
									+ cellIDwLac.get(id).lac
									+ " distance: "
									+ cc.LatLngDistance(
											Double.parseDouble(s.lat),
											Double.parseDouble(s.lng),
											cellIDwLac.get(id).lat,
											cellIDwLac.get(id).lng));
						}
					} else {
						cellIDwLac.put(id,
								new LacLatLng(lac, Double.parseDouble(s.lat),
										Double.parseDouble(s.lng)));
					}
				}
			}
		}
	}

	public void TestProducetrainsequence() {
		System.out.println("TestProducetrainsequence");
		FileConverter fc = new FileConverter();

		BusControl busController = new BusControl();
		// busController.ShowDistance(fc.listOfDevices);
		// what is this
		LinkedList<Sequence> seqs = busController
				.produceTrainSequence(fc.listOfDevices);

		File trainFolder = new File(CONSTANTS.TRAIN_DATA_FOLDER);
		File testFolder = new File(CONSTANTS.TEST_DATA_FOLDER);
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);

		if (!trainFolder.exists() || !trainFolder.isDirectory()) {
			System.out.println("Please recheck TRAIN_DATA_FOLDER at "
					+ CONSTANTS.TRAIN_DATA_FOLDER);
			return;
		}

		if (!testFolder.exists() || !testFolder.isDirectory()) {
			System.out.println("Please recheck TEST_DATA_FOLDER at "
					+ CONSTANTS.TEST_DATA_FOLDER);
			return;
		}

		if (!resultFolder.exists()) {
			resultFolder.mkdir();
			System.out.println("RESULT_FOLDER created at "
					+ CONSTANTS.TEST_DATA_FOLDER);
		}

		// CREATE RESULT LOG FILE
		int resultfileindex = resultFolder.list().length;
		try {
			Writer out2 = new BufferedWriter(
					new OutputStreamWriter(new FileOutputStream(
							CONSTANTS.RESULT_FOLDER + resultfileindex
									+ "_testproduceseq.txt"), "UTF-8"));

			Sample tmpsample;
			Sequence tmp1;
			for (int i = 0; i < seqs.size(); i++) {
				tmp1 = seqs.get(i);
				if (tmp1 != null) {
					for (int k = 0; k < tmp1.size(); k++) {
						tmpsample = fc.listOfDevices.get(i).sampleList.get(k);
						out2.write(tmpsample.lat + " " + tmpsample.lng + " ");
					}
					out2.write("\n");
					for (SequenceElement seqel : tmp1.sequenceinFile) {
						out2.write((int) seqel.State + " ");
					}
					out2.write("\n");

					for (SequenceElement seqel : tmp1.sequenceinFile) {
						out2.write((int) seqel.State + " ");
					}
					out2.write("\n");
					out2.write("\n");

				}

			}
			out2.close();
		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	// METHODS IMPLEMENTATION
	public void trainAndTestMultiplecellOneRun(int sequence_length) {
		System.out
				.println("trainAndTestMultiplecellOneRun(int sequence_length) "
						+ sequence_length);
		FileConverter fc = new FileConverter();

		BusControl busController = new BusControl();
		// busController.ShowDistance(fc.listOfDevices);
		// what is this
		LinkedList<Sequence> seqs1 = busController
				.produceTrainSequence(fc.listOfDevices);
		LinkedList<Sequence> seqs2 = busController.produceTrainSequence(
				fc.listOfDevices, 1);
		LinkedList<Sequence> seqs3 = busController.produceTrainSequence(
				fc.listOfDevices, 2);
		HMM mHMM1 = new HMM();
		mHMM1.train(seqs1);
		HMM mHMM2 = new HMM();
		mHMM2.train(seqs2);
		HMM mHMM3 = new HMM();
		mHMM3.train(seqs3);

		File trainFolder = new File(CONSTANTS.TRAIN_DATA_FOLDER);
		File testFolder = new File(CONSTANTS.TEST_DATA_FOLDER);
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);

		if (!trainFolder.exists() || !trainFolder.isDirectory()) {
			System.out.println("Please recheck TRAIN_DATA_FOLDER at "
					+ CONSTANTS.TRAIN_DATA_FOLDER);
			return;
		}

		if (!testFolder.exists() || !testFolder.isDirectory()) {
			System.out.println("Please recheck TEST_DATA_FOLDER at "
					+ CONSTANTS.TEST_DATA_FOLDER);
			return;
		}

		if (!resultFolder.exists()) {
			resultFolder.mkdir();
			System.out.println("RESULT_FOLDER created at "
					+ CONSTANTS.TEST_DATA_FOLDER);
		}

		// CREATE RESULT LOG FILE
		int resultfileindex = resultFolder.list().length;
		try {
			Writer out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER
							+ resultfileindex + ".txt"), "UTF-8"));
			Writer out2 = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER
							+ resultfileindex + "_excel.txt"), "UTF-8"));

			// TRAIN
			// mHMM.emitAll();
			out.write(mHMM1.toString());
			mHMM1.emitAll();
			int countFalse = 0;
			int countFalseElement = 0;
			int countFalseLast = 0;

			Sample tmpsample;
			Random randomGenerator = new Random();

			for (int i = 0; i < CONSTANTS.nExample; i++) {
				// TODO: check seqs.size()
				int fileindex = randomGenerator.nextInt(seqs1.size()) - 1;
				if (fileindex == -1) {
					fileindex = 0;
				}
				int start = randomGenerator.nextInt(seqs1.get(fileindex).size()
						- sequence_length - 1);

				Sequence testdata1 = new Sequence();
				Sequence testdata2 = new Sequence();
				Sequence testdata3 = new Sequence();
				for (int j = start; j < start + sequence_length; j++) {
					testdata1.add(seqs1.get(fileindex).get(j));
					testdata2.add(seqs2.get(fileindex).get(j));
					testdata3.add(seqs3.get(fileindex).get(j));
				}
				System.out.println("TEST " + i);
				out.write("\nTEST" + i + "\n");
				Sequence tmp1 = mHMM1.predict(testdata1);
				Sequence tmp2 = mHMM2.predict(testdata2);
				Sequence tmp3 = mHMM3.predict(testdata3);

				int index = 0;
				for (SequenceElement e : tmp1.sequenceinFile) {

					if (e.State == tmp2.sequenceinFile.get(index).State) {
						continue;
					}
					if (e.State == tmp3.sequenceinFile.get(index).State) {
						continue;
					}
					if (tmp2.sequenceinFile.get(index).State == tmp3.sequenceinFile
							.get(index).State) {
						e.State = tmp2.sequenceinFile.get(index).State;
					}

					++index;
				}

				if (testdata1.compareto(tmp1) != 0) {
					++countFalse;
					countFalseElement += testdata1.compareto(tmp1);
					// testdata1.printSequence();
					out.write(testdata1.toString() + "\n");

					if (tmp1 != null) {
						out.write(tmp1.toString() + "\n");
						// tmp1.printSequence();

						for (int k = 0; k < sequence_length; k++) {
							tmpsample = fc.listOfDevices.get(fileindex).sampleList
									.get(start + k);
							out2.write(tmpsample.lat + " " + tmpsample.lng
									+ " ");
						}
						out2.write("\n");
						for (SequenceElement seqel : testdata1.sequenceinFile) {
							out2.write(seqel.State + " ");
						}
						out2.write("\n");

						for (SequenceElement seqel : tmp1.sequenceinFile) {
							out2.write(seqel.State + " ");
						}
						out2.write("\n");
						out2.write("\n");

					}
				}
				if (testdata1.compareLast(tmp1) == false) {
					++countFalseLast;
				}

			}
			out.write("Number of test: " + CONSTANTS.nExample + "\n");
			out.write("Number of positive: "
					+ (CONSTANTS.nExample - countFalse) + "\n");
			out.write("Accuracy: "
					+ (1.0 * (CONSTANTS.nExample - countFalse)
							/ CONSTANTS.nExample * 100.0) + "\n");
			out.write("Number of Element in test: " + CONSTANTS.nExample
					* sequence_length + "\n");
			out.write("Number of positive: "
					+ (CONSTANTS.nExample * sequence_length - countFalseElement)
					+ "\n");
			out.write("Accuracy: "
					+ (1.0
							* (CONSTANTS.nExample * sequence_length - countFalseElement)
							/ (CONSTANTS.nExample * sequence_length) * 100.0)
					+ "\n");
			out.write("CountFalseElement/CountFalse: " + 1.0
					* countFalseElement / countFalse);

			out.write("CountFalseLast: " + countFalseLast + '\n');
			out.write("CountFalseLast Accuracy: " + 1.0 * countFalseLast
					/ CONSTANTS.nExample + '\n');

			out.close();

		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	public void trainMainCell() {
		// HEADER ???
		FileConverter fc = new FileConverter();

		BusControl busController = new BusControl();
		// busController.ShowDistance(fc.listOfDevices);

		LinkedList<Sequence> seqs = busController
				.produceTrainSequence(fc.listOfDevices);
		mHMM = new HMM();
		mHMM.train(seqs);

	}

	public void testWithPrinting(int sequence_length) {

		try {
			double[] result = testWithoutPrinting(sequence_length, true);
			System.out.println("Number of test: " + result[0]);
			System.out.println("Number of positive: " + result[1]);
			System.out.println("Accuracy: " + result[2]);
			System.out.println("Number of Element in test: " + result[3]);
			System.out.println("Number of positive: " + result[4]);
			System.out.println("Accuracy: " + result[5]);
			System.out.println("CountFalseElement/CountFalse: " + result[6]);
			System.out.println("CountFalseLast: " + result[7]);
			System.out.println("CountFalseLast Accuracy: " + result[8]);
		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	public double[] testWithoutPrinting(int sequence_length,
			boolean writeToFileEachRun) {
		// CREATE RESULT LOG FILE
		double[] result = new double[9];
		checkFolders();
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);
		int resultfileindex = resultFolder.list().length;
		try {

			// TRAIN
			// mHMM.emitAll();
			Writer out = null;

			if (writeToFileEachRun) {
				out = new BufferedWriter(new OutputStreamWriter(
						new FileOutputStream(CONSTANTS.RESULT_FOLDER
								+ resultfileindex + ".txt"), "UTF-8"));
			}
			int countFalse = 0;
			int countFalseElement = 0;
			int countFalseLast = 0;

			FileConverter fc = new FileConverter();
			BusControl busController = new BusControl();
			// busController.ShowDistance(fc.listOfDevices);

			LinkedList<Sequence> seqs = busController
					.produceTrainSequence(fc.listOfDevices);

			Random randomGenerator = new Random();

			for (int i = 0; i < CONSTANTS.nExample; i++) {
				// TODO: check seqs.size()
				int fileindex = randomGenerator.nextInt(seqs.size()) - 1;
				if (fileindex == -1) {
					fileindex = 0;
				}
				int start = randomGenerator.nextInt(seqs.get(fileindex).size()
						- sequence_length - 1);
				Sequence testdata = new Sequence();
				for (int j = start; j < start + sequence_length; j++) {
					testdata.add(seqs.get(fileindex).get(j));
				}
				Sequence tmp2 = mHMM.predict(testdata);

				if (testdata.compareto(tmp2) != 0) {
					++countFalse;
					countFalseElement += testdata.compareto(tmp2);
					if (writeToFileEachRun) {
						out.write(testdata.toString());
						out.write(tmp2.toString() + "\n");
					}
				}

				if (testdata.compareLast(tmp2) == false) {
					++countFalseLast;
				}

			}

			result[0] = CONSTANTS.nExample;
			result[1] = (CONSTANTS.nExample - countFalse);
			result[2] = (1.0 * (CONSTANTS.nExample - countFalse)
					/ CONSTANTS.nExample * 100.0);
			result[3] = CONSTANTS.nExample * sequence_length;
			result[4] = (CONSTANTS.nExample * sequence_length - countFalseElement);
			result[5] = (1.0
					* (CONSTANTS.nExample * sequence_length - countFalseElement)
					/ (CONSTANTS.nExample * sequence_length) * 100.0);
			result[6] = (1.0 * countFalseElement / countFalse);
			result[7] = countFalseLast;
			result[8] = 1.0 * countFalseLast / CONSTANTS.nExample;
			if (writeToFileEachRun) {
				out.write("Number of test: " + result[0] + "\n");
				out.write("Number of positive: " + result[1] + "\n");
				out.write("Accuracy: " + result[2] + "\n");
				out.write("Number of Element in test: " + result[3] + "\n");
				out.write("Number of positive: " + result[4] + "\n");
				out.write("Accuracy: " + result[5] + "\n");
				out.write("CountFalseElement/CountFalse: " + result[6] + "\n");
				out.write("CountFalseLast: " + result[7] + "\n");
				out.write("CountFalseLast Accuracy: " + result[8] + "\n");
				out.flush();
				out.close();
			}

			return result;

		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

		return null;

	}

	public void testMultipleRuns(boolean writeToFileEachRun) {
		checkFolders();
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);
		int resultfileindex = resultFolder.list().length;
		Writer out = null;

		// file header
		try {
			out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(CONSTANTS.RESULT_FOLDER
							+ resultfileindex + "_50runs.txt"), "UTF-8"));

			out.write("i \t Number of test \t Number of positive \t Accuracy \t Number of Element in test \t "
					+ "Number of positive: \t Accuracy: \t CountFalseElement/CountFalse\n");
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

		// file contains
		try {
			for (int i = 2; i < 10; ++i) {
				System.out.println("Testing " + i);
				double[] result = testWithoutPrinting(i, writeToFileEachRun);

				out.write(i + "\t" + result[0] + "\t" + result[1] + "\t"
						+ result[2] + "\t" + result[3] + "\t" + result[4]
						+ "\t" + result[5] + "\t" + result[6] + "\t"
						+ result[7] + "\t" + result[8] + "\n");
				out.flush();
				System.out.println("DONE Testing " + i);

			}
			out.close();
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}

	}

	// test cut velocity
	public void checkFolders() {
		// CREATE FOLDERS
		File trainFolder = new File(CONSTANTS.TRAIN_DATA_FOLDER);
		File testFolder = new File(CONSTANTS.TEST_DATA_FOLDER);
		File resultFolder = new File(CONSTANTS.RESULT_FOLDER);

		if (!trainFolder.exists() || !trainFolder.isDirectory()) {
			System.out.println("Please recheck TRAIN_DATA_FOLDER at "
					+ CONSTANTS.TRAIN_DATA_FOLDER);
			return;
		}

		if (!testFolder.exists() || !testFolder.isDirectory()) {
			System.out.println("Please recheck TEST_DATA_FOLDER at "
					+ CONSTANTS.TEST_DATA_FOLDER);
			return;
		}

		if (!resultFolder.exists()) {
			resultFolder.mkdir();
			System.out.println("RESULT_FOLDER created at "
					+ CONSTANTS.TEST_DATA_FOLDER);
		}
	}
}
